# -*- coding: utf-8 -*-
# -*- frozen_string_literal: true -*-
require_relative "./../_config/minitest"
require "lorca"

module FakeExpansion
  module LorcaPlugin
    def fake
    end
  end

  module LorcaExtension
    def warn msg="extension deprecation"
      super msg
    end
  end
end

module NoPluginExpansion
  LorcaExtension = Module.new
end

module NoExtensionExpansion
  LorcaPlugin = Module.new
end

module LoadsDependenciesExpansion
  def self.load_dependencies mod, **_
    mod.send(:include, Module.new {
        def whatever
          true
        end
      }
    )
  end
end

module ConfigurableExpansion
  def self.configure mod, **stns#, &block
    mod.settings[:something] = stns[:something]
  end
end

class TestLorcaCoreExtension < Minitest::Test

  parallelize_me!

  def test_lorca_can_add_expansions
    Lorca.add FakeExpansion
    lorca = Lorca.new

    assert_respond_to lorca, :fake
  end

  def test_extensions_can_override_warn_for_custom_warnings
    Lorca.add FakeExpansion
    _, warning = capture_io { Lorca.warn }

    assert_match %r/extension deprecation/, warning
  end

  def test_add_expansion_with_no_plugin
    assert Lorca.add(NoPluginExpansion)
  end

  def test_add_expansion_with_no_extension
    assert Lorca.add(NoExtensionExpansion)
  end

  def test_add_expansions_in_succession
    assert Lorca.add(Module.new).add(Module.new)
  end

  def test_expansion_can_load_dependencies_when_needed
    Lorca.add LoadsDependenciesExpansion
    assert Lorca.new.whatever
  end

  def test_can_configure_expansion_when_added
    Lorca.add ConfigurableExpansion, something: "configured"
    assert_equal "configured", Lorca.settings[:something]
  end
end

class TestLorcaCorePlugin < Minitest::Test

  parallelize_me!

  def setup
    @lorca = Lorca.new
  end

  def test_has_immutable_settings
    assert_frozen @lorca.settings
  end

  def test_rolls_dice
    assert_includes 1..6, @lorca.roll_dice
  end

  def test_rolls_a_word_id_given_n_dice
    n_dice = 5
    assert_match %r/[1-6]{#{n_dice}}/, @lorca.roll_word_id(dice: n_dice)
  end

  def test_fails_to_roll_word_id_given_invalid_number_of_dice
    [0, -1, nil, Object.new].map do |invalid_dice|
      e = assert_raises(Lorca::LorcaDiceError) {
        @lorca.roll_word_id dice: invalid_dice
      }
      assert_match %r"should be a positive integer"i, e.message
    end
  end

  def test_validates_counter_is_within_range
    num = 8
    assert_equal num, @lorca.validate_counter(num, range: 1..10)
  end

  def test_fails_to_validate_counter_out_of_range
    err_min = assert_raises(Lorca::LorcaCounterError) do
      @lorca.validate_counter 3, range: 4..7
    end

    err_max = assert_raises(Lorca::LorcaCounterError) do
      @lorca.validate_counter 8, range: 4..7
    end

    message = %r"\Acounter out of range\z"i
    assert_match message, err_min.message
    assert_match message, err_max.message
  end

  def test_validates_a_range
    range = 3..6
    assert_equal range, @lorca.validate_range(range)
  end

  def test_fails_to_validate_an_invalid_range
    [3..2, 4..0, -1..4, "a".."z"].map do |invalid_range|
      e = assert_raises(Lorca::LorcaRangeError) {
        @lorca.validate_range invalid_range
      }
      assert_match %r"\Ashould be an end-inclusive range of positive integers\z"i,
        e.message
    end
  end

  def test_validates_positive_integer
    num = 8
    assert_equal num, @lorca.validate_positive_integer(num)
  end

  def test_fails_to_validate_positive_integer_given_invalid_input
    [-1, 0, 2.1, Object.new, nil].map do |invalid_counter|
      e = assert_raises(Lorca::LorcaCounterError) {
        @lorca.validate_positive_integer invalid_counter
      }

      assert_match %r"\Ashould be a positive integer\z"i, e.message
    end
  end
end
